/************************************************************************
                                                                     
   Xceed Ultimate ListBox for Silverlight                                                                                                                                            
   Copyright (C) 2010 Xceed Software Inc.    
                                                                     
   This program is provided to you under the terms of the GNU General Public  
   License version 2 as published by the Free Software Foundation. 
        
   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY, without even the implied warranty of 
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License along 
   with this program, if not, write to the Free Software Foundation, Inc., 
   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

   This program can be provided to you by Xceed Software Inc. under a
   proprietary commercial license agreement for use in non-Open Source
   projects. Visit Xceed.com to find the commercial edition of 
   Xceed Ultimate ListBox for Silverlight.                                    
                                                                      
 **********************************************************************/

using System;
using System.Windows;
using Xceed.Silverlight.Data;
using System.ComponentModel;

namespace Xceed.Silverlight.ListBox
{
  public abstract class ListBoxContainerConfigurationBase : DependencyObject , ISupportInitialize
  {
    #region Static Members

    private static readonly Size EstimatedSize = new Size( 20, 20 );

    #endregion Static Members

    #region Constructors

    internal ListBoxContainerConfigurationBase()
    {
    }

    internal ListBoxContainerConfigurationBase( ListBoxContainerConfigurationBase template )
    {
      this.IsSticky = template.IsSticky;
      m_cachedSize = template.CachedSize;

      this.ContainerStyle = template.ContainerStyle;
      this.ContentTemplate = template.ContentTemplate;

      this.LayoutMode = template.LayoutMode;
    }

    #endregion Constructors

    #region ConfigurationType Property

    public ConfigurationType ConfigurationType
    {
      get
      {
        return this.GetConfigurationType();
      }
    }

    #endregion

    #region CanDrag Internal Property

    internal virtual bool CanDrag
    {
      get
      {
        return true;
      }
    }

    #endregion

    #region CanStick Property

    public virtual bool CanStick
    {
      get
      {
        return false;
      }
    }

    #endregion CanStick Property

    #region IsSticky Property

    public static readonly DependencyProperty IsStickyProperty = DependencyProperty.Register(
      "IsSticky",
      typeof( bool ),
      typeof( ListBoxContainerConfigurationBase ),
      new PropertyMetadata(
        true,
        new PropertyChangedCallback( ListBoxContainerConfigurationBase.OnIsStickyChanged ) ) );

    public bool IsSticky
    {
      get
      {
        return ( bool )this.GetValue( ListBoxContainerConfigurationBase.IsStickyProperty );
      }
      set
      {
        this.SetValue( ListBoxContainerConfigurationBase.IsStickyProperty, value );
      }
    }

    private static void OnIsStickyChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
    {
      ListBoxContainerConfigurationBase containerConfiguration = ( ListBoxContainerConfigurationBase )sender;
      bool isSticky = ( bool )e.NewValue;

      if( isSticky && !containerConfiguration.CanStick )
      {
        containerConfiguration.IsSticky = false;
        throw new ArgumentException( "An attempt was made to set as sticky a ContainerConfiguration that does not support it." );
      }
    }

    #endregion IsSticky Property

    #region ContentTemplate Property

    public static readonly DependencyProperty ContentTemplateProperty = DependencyProperty.Register(
       "ContentTemplate",
       typeof( DataTemplate ),
       typeof( ListBoxContainerConfigurationBase ),
       new PropertyMetadata( null, new PropertyChangedCallback( ListBoxContainerConfigurationBase.OnContentTemplateChanged ) ) );

    public DataTemplate ContentTemplate
    {
      get
      {
        return ( DataTemplate )this.GetValue( ListBoxContainerConfigurationBase.ContentTemplateProperty );
      }
      set
      {
        this.SetValue( ListBoxContainerConfigurationBase.ContentTemplateProperty, value );
      }
    }

    private static void OnContentTemplateChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
    {
      ListBoxContainerConfigurationBase containerConfigurationBase = ( ListBoxContainerConfigurationBase )sender;

      if( containerConfigurationBase.ContentTemplateChanged != null )
        containerConfigurationBase.ContentTemplateChanged( containerConfigurationBase, EventArgs.Empty );
    }

    internal event EventHandler ContentTemplateChanged;

    #endregion ContentTemplate Property

    #region ContainerStyle Property

    public static readonly DependencyProperty ContainerStyleProperty = DependencyProperty.Register(
       "ContainerStyle",
       typeof( Style ),
       typeof( ListBoxContainerConfigurationBase ),
       new PropertyMetadata( null, new PropertyChangedCallback( ListBoxContainerConfigurationBase.OnContainerStyleChanged ) ) );

    public Style ContainerStyle
    {
      get
      {
        return ( Style )this.GetValue( ListBoxContainerConfigurationBase.ContainerStyleProperty );
      }
      set
      {
        this.SetValue( ListBoxContainerConfigurationBase.ContainerStyleProperty, value );
      }
    }

    private static void OnContainerStyleChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
    {
      Style newStyle = e.NewValue as Style;
      ListBoxContainerConfigurationBase containerConfigurationBase = ( ListBoxContainerConfigurationBase )sender;

      if( containerConfigurationBase.ContainerStyleChanged != null )
        containerConfigurationBase.ContainerStyleChanged( containerConfigurationBase, EventArgs.Empty );
    }

    internal event EventHandler ContainerStyleChanged;

    #endregion ItemContainerStyle Property

    #region LayoutMode Property

    public static readonly DependencyProperty LayoutModeProperty = DependencyProperty.Register( 
      "LayoutMode", 
      typeof( ContainerLayoutMode ), 
      typeof( ListBoxContainerConfigurationBase ), 
      new PropertyMetadata( ContainerLayoutMode.Default ) );

    public ContainerLayoutMode LayoutMode
    {
      get 
      { 
        return ( ContainerLayoutMode )this.GetValue( ListBoxContainerConfigurationBase.LayoutModeProperty ); 
      }
      set 
      { 
        this.SetValue( ListBoxContainerConfigurationBase.LayoutModeProperty, value ); 
      }
    }

    #endregion


    #region Size Caching

    internal Size PrepareSize
    {
      get
      {
        if( Size.Equals( Size.Empty, m_cachedSize ) )
        {
          // Cache not initialized.
          // Use a estimated size instead.
          return ListBoxContainerConfigurationBase.EstimatedSize;
        }

        return m_cachedSize;
      }
    }

    internal Size CachedSize
    {
      get
      {
        return m_cachedSize;
      }
    }

    internal bool UpdateCachedSize( ListBoxContainer container )
    {
      if( container == null )
        throw new ArgumentNullException( "container" );

      DataPath dataPath = container.DataPath;
      Size oldCachedSize = this.CachedSize;
      Size updatedCachedSize;

      Size desiredSize;

      if( ( dataPath != null ) && ( !dataPath.LastChild.IsLoaded ) )
      {
        // The ListBoxGroupHeader should never have a desired Height/Width of 0.
        if( container.GetType() == typeof( ListBoxGroupHeader ) )
        {
          desiredSize = container.DesiredSize;
        }
        else
        {
          // Ensure to at least set m_cachedSize to the EstimatedSize
          desiredSize = ListBoxContainerConfigurationBase.EstimatedSize;
        }
      }
      else
      {
        desiredSize = container.DesiredSize;
      }

      updatedCachedSize = new Size(
        Math.Max( oldCachedSize.Width, desiredSize.Width ),
        Math.Max( oldCachedSize.Height, desiredSize.Height ) );

      if( Size.Equals( oldCachedSize, updatedCachedSize ) )
        return false;

      m_cachedSize = updatedCachedSize;

      this.OnCachedSizeChanged( new CachedSizeChangedEventArgs( oldCachedSize, updatedCachedSize ) );

      return true;
    }

    internal void OnCachedSizeChanged( CachedSizeChangedEventArgs e )
    {
      if( this.CachedSizeChanged != null )
        this.CachedSizeChanged( this, e );
    }

    internal event EventHandler<CachedSizeChangedEventArgs> CachedSizeChanged;

    #endregion Size Caching

    #region Internal Methods

    internal abstract ListBoxContainer CreateContainer();

    internal abstract ConfigurationType GetConfigurationType();

    protected internal abstract ListBoxContainerConfigurationBase CreateInstance();

    internal abstract void PrepareContainerForItem( ListBoxContainer container, object item );

    #endregion Internal Methods

    #region PRIVATE FIELDS

    private Size m_cachedSize = Size.Empty;

    #endregion PRIVATE FIELDS

    #region ISupportInitialize Members

    protected virtual void BeginInitCore()
    {
    }

    protected virtual void EndInitCore()
    {

    }

    void ISupportInitialize.BeginInit()
    {
      this.BeginInitCore();
    }

    void ISupportInitialize.EndInit()
    {
      this.EndInitCore();
    }

    #endregion
  }
}
